This file is used to generate a dataset containing only ORS and IFE basal cells.

library(dplyr)
library(patchwork)
library(ggplot2)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

save_name = "ors_ifeb"
out_dir = "."

We load the sample information :

sample_info = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_sample_info.rds"))
project_names_oi = sample_info$project_name

graphics::pie(rep(1, nrow(sample_info)),
              col = sample_info$color,
              labels = sample_info$project_name)

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_color_markers.rds"))

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(angle = 30, hjust = 1))

Make ors_ifeb dataset

Atlas

We load the combined dataset containing all cell types from all samples :

sobj = readRDS(paste0(out_dir, "/../../3_combined/hs_hd_sobj.rds"))
sobj
## An object of class Seurat 
## 20003 features across 12111 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

We represent cells in the tSNE :

name2D = "harmony_38_tsne"

We smooth cell type annotation at a cluster level :

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]

We look gene markers expression level, cell annotation and cluster-smoothed annotation on the projection, to locate ors_ifeb cells :

ors_ifeb_markers = c("KRT16", "EHF", "ALDH3A1")
ors_ifeb_cell_type = c("ORS", "IFE basal")
color_markers[!(names(color_markers) %in% ors_ifeb_cell_type)] = "gray92"

# Feature Plot
plot_list = lapply(ors_ifeb_markers, FUN = function(one_gene) {
  p = Seurat::FeaturePlot(sobj, reduction = name2D,
                          features = one_gene) +
    Seurat::NoAxes() +
    ggplot2::scale_color_gradientn(colors = aquarius:::color_gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.subtitle = element_text(hjust = 0.5))
  return(p)
})

# Cell type annotation
plot_list[[length(plot_list) + 1]] = Seurat::DimPlot(sobj, group.by = "cell_type",
                                                     cols = color_markers, reduction = name2D,
                                                     order = save_name) +
  ggplot2::labs(title = "Cell annotation",
                subtitle = paste0(sum(sobj$cell_type %in% ors_ifeb_cell_type),
                                  " cells")) +
  Seurat::NoAxes() + Seurat::NoLegend() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

# Cluster-smoothed annotation
plot_list[[length(plot_list) + 1]] = Seurat::DimPlot(sobj,
                                                     reduction = name2D,
                                                     group.by = "cluster_type") +
  ggplot2::scale_color_manual(values = c(unname(unlist(color_markers[ors_ifeb_cell_type])),
                                         rep("gray92", length(color_markers) - length(ors_ifeb_cell_type))),
                              breaks = c(ors_ifeb_cell_type, setdiff(names(color_markers), ors_ifeb_cell_type))) +
  ggplot2::labs(title = "Cluster annotation",
                subtitle = paste0(sum(sobj$cluster_type %in% ors_ifeb_cell_type),
                                  " cells")) +
  Seurat::NoAxes() + Seurat::NoLegend() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1)

Due to clustering that badly separate ORS from IFE, there is a mis-selection of cells of interest : false positive. However, annotation smoothing adds a lot of (all) false negative and clean the annotation. We extract cells of interest based on clustering. Then, we generate a new cluster to remove false positive.

Combined dataset

We extract cells of interest based on the clustering :

sobj = subset(sobj, cluster_type %in% ors_ifeb_cell_type)
sobj
## An object of class Seurat 
## 20003 features across 3532 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

We remove all things that were calculated based on the full atlas :

sobj = Seurat::DietSeurat(sobj)
sobj
## An object of class Seurat 
## 20003 features across 3532 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)

Clean metadata

We keep a subset of meta.data and reset levels :

sobj@meta.data = sobj@meta.data[, c("orig.ident", "nCount_RNA", "nFeature_RNA", "log_nCount_RNA",
                                    "project_name", "sample_identifier", "sample_type",
                                    "laboratory", "location", "Seurat.Phase", "cyclone.Phase",
                                    "percent.mt", "percent.rb", "cell_type")]

sobj$orig.ident = factor(sobj$orig.ident, levels = levels(sample_info$project_name))
sobj$project_name = factor(sobj$project_name, levels = levels(sample_info$project_name))
sobj$sample_identifier = factor(sobj$sample_identifier, levels = levels(sample_info$sample_identifier))
sobj$sample_type = factor(sobj$sample_type, levels = levels(sample_info$sample_type))

summary(sobj@meta.data)
##    orig.ident    nCount_RNA     nFeature_RNA  log_nCount_RNA    project_name
##  2021_31:396   Min.   :  711   Min.   : 500   Min.   : 6.572   2021_31:396  
##  2021_36:163   1st Qu.: 3500   1st Qu.:1076   1st Qu.: 8.161   2021_36:163  
##  2021_41:628   Median : 9494   Median :2574   Median : 9.159   2021_41:628  
##  2022_03:995   Mean   :12083   Mean   :2593   Mean   : 8.971   2022_03:995  
##  2022_14:667   3rd Qu.:16744   3rd Qu.:3738   3rd Qu.: 9.726   2022_14:667  
##  2022_01:324   Max.   :74969   Max.   :7123   Max.   :11.225   2022_01:324  
##  2022_02:359                                                   2022_02:359  
##  sample_identifier sample_type  laboratory          location        
##  HS_1:396          HS:2849     Length:3532        Length:3532       
##  HS_2:163          HD: 683     Class :character   Class :character  
##  HS_3:628                      Mode  :character   Mode  :character  
##  HS_4:995                                                           
##  HS_5:667                                                           
##  HD_1:324                                                           
##  HD_2:359                                                           
##  Seurat.Phase       cyclone.Phase        percent.mt        percent.rb    
##  Length:3532        Length:3532        Min.   : 0.0000   Min.   : 2.257  
##  Class :character   Class :character   1st Qu.: 0.2583   1st Qu.:22.168  
##  Mode  :character   Mode  :character   Median : 3.6962   Median :27.101  
##                                        Mean   : 3.9238   Mean   :26.440  
##                                        3rd Qu.: 5.9263   3rd Qu.:31.222  
##                                        Max.   :19.8012   Max.   :46.017  
##                                                                          
##                 cell_type   
##  IFE basal           :1749  
##  ORS                 :1506  
##  IFE granular spinous: 165  
##  HF-SCs              :  58  
##  proliferative       :  24  
##  sebocytes           :  20  
##  (Other)             :  10

Processing

Metadata

How many cells by sample ?

table(sobj$project_name)
## 
## 2021_31 2021_36 2021_41 2022_03 2022_14 2022_01 2022_02 
##     396     163     628     995     667     324     359

We represent this information as a barplot :

aquarius::plot_barplot(df = table(sobj$project_name,
                                  sobj$cell_type) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("project_name", "cell_type", "nb_cells")),
                       x = "project_name", y = "nb_cells", fill = "cell_type",
                       position = position_stack()) +
  ggplot2::scale_fill_manual(values = color_markers,
                             breaks = names(color_markers),
                             name = "Cell type")

Remove false positive

We normalize gene expression for remaining cells :

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize")
sobj = Seurat::FindVariableFeatures(sobj, nfeatures = 3000)
sobj = Seurat::ScaleData(sobj)

sobj
## An object of class Seurat 
## 20003 features across 3532 samples within 1 assay 
## Active assay: RNA (20003 features, 3000 variable features)

We perform a PCA :

sobj = Seurat::RunPCA(sobj,
                      assay = "RNA",
                      reduction.name = "RNA_pca",
                      npcs = 100,
                      seed.use = 1337L)
sobj
## An object of class Seurat 
## 20003 features across 3532 samples within 1 assay 
## Active assay: RNA (20003 features, 3000 variable features)
##  1 dimensional reduction calculated: RNA_pca

We choose the number of dimensions such that they summarize 60 % of the variability :

stdev = sobj@reductions[["RNA_pca"]]@stdev
stdev_prop = cumsum(stdev)/sum(stdev)
ndims = which(stdev_prop > 0.60)[1]
ndims
## [1] 48

We can visualize this on the elbow plot :

elbow_p = Seurat::ElbowPlot(sobj, ndims = 100, reduction = "RNA_pca") +
  ggplot2::geom_point(x = ndims, y = stdev[ndims], col = "red")
x_text = ggplot_build(elbow_p)$layout$panel_params[[1]]$x$get_labels() %>% as.numeric()
elbow_p = elbow_p +
  ggplot2::scale_x_continuous(breaks = sort(c(x_text, ndims)), limits = c(0, 100))
x_color = ifelse(ggplot_build(elbow_p)$layout$panel_params[[1]]$x$get_labels() %>%
                   as.numeric() %>% round(., 2) == round(ndims, 2), "red", "black")
elbow_p = elbow_p +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = element_text(color = x_color))

elbow_p

We generate a tSNE and a UMAP with 48 principal components :

sobj = Seurat::RunTSNE(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_tsne"))

sobj = Seurat::RunUMAP(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_umap"))

We can visualize the two representations :

tsne = Seurat::DimPlot(sobj, group.by = "project_name",
                       reduction = paste0("RNA_pca_", ndims, "_tsne")) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  Seurat::NoAxes() + ggplot2::ggtitle("PCA - tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "project_name",
                       reduction = paste0("RNA_pca_", ndims, "_umap")) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  Seurat::NoAxes() + ggplot2::ggtitle("PCA - UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

We generate a clustering from the PCA :

reduction_name = "RNA_pca"

sobj = Seurat::FindNeighbors(sobj, reduction = reduction_name)
sobj = Seurat::FindClusters(sobj, resolution = 1.7)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 3532
## Number of edges: 111658
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7631
## Number of communities: 23
## Elapsed time: 0 seconds
cluster_plot = Seurat::DimPlot(sobj, label = TRUE,
                               reduction = paste0(reduction_name, "_", ndims, "_umap")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1)
cluster_plot

We look at key markers for ors_ifeb cells, and for eventual contamination :

plot_list = lapply(c("nFeature_RNA", ors_ifeb_markers,
                     c("SPINK5", "KRT1", "KRTDAP", "CIDEA")), FUN = function(one_gene) {
                       Seurat::FeaturePlot(sobj, features = one_gene,
                                           reduction = paste0(reduction_name, "_", ndims, "_umap")) +
                         ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
                         ggplot2::theme(aspect.ratio = 1) +
                         Seurat::NoAxes() + Seurat::NoLegend()
                     })

patchwork::wrap_plots(plot_list, nrow = 2)

We select clusters based on KRTDAP expression :

mean_score_thresh = 1

sobj$KRTDAP_expr = Seurat::FetchData(sobj, "KRTDAP")
score_by_clusters = sobj@meta.data %>%
  dplyr::select(seurat_clusters, KRTDAP_expr) %>%
  dplyr::group_by(seurat_clusters) %>%
  dplyr::summarise(avg_score = mean(KRTDAP_expr)) %>%
  as.data.frame()

ggplot2::ggplot(score_by_clusters, aes(x = seurat_clusters, y = avg_score)) +
  ggplot2::geom_point() +
  ggplot2::geom_hline(yintercept = mean_score_thresh, col = "red") +
  ggplot2::labs(x = "Cluster ID", y = "Mean KRTDAP expression",
                title = "Proportion of cells by cluster") +
  ggplot2::theme_classic() +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"))

We remove clusters above threshold :

clusters_to_remove = score_by_clusters %>%
  dplyr::filter(avg_score > mean_score_thresh) %>%
  dplyr::pull(seurat_clusters) %>%
  as.character()

sobj = subset(sobj, idents = clusters_to_remove, invert = TRUE)
sobj
## An object of class Seurat 
## 20003 features across 3416 samples within 1 assay 
## Active assay: RNA (20003 features, 3000 variable features)
##  3 dimensional reductions calculated: RNA_pca, RNA_pca_48_tsne, RNA_pca_48_umap

We remove all reductions in this dataset :

sobj = Seurat::DietSeurat(sobj, assays = "RNA")
sobj
## An object of class Seurat 
## 20003 features across 3416 samples within 1 assay 
## Active assay: RNA (20003 features, 3000 variable features)

How many cells by sample ?

table(sobj$project_name)
## 
## 2021_31 2021_36 2021_41 2022_03 2022_14 2022_01 2022_02 
##     368     160     607     981     637     318     345

We represent this information as a barplot :

aquarius::plot_barplot(df = table(sobj$project_name,
                                  sobj$cell_type) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("project_name", "cell_type", "nb_cells")),
                       x = "project_name", y = "nb_cells", fill = "cell_type",
                       position = position_fill()) +
  ggplot2::scale_fill_manual(values = color_markers,
                             breaks = names(color_markers),
                             name = "Cell type")

No contamination remaining !

Projection

We remove genes that are expressed in less than 5 cells :

sobj = aquarius::filter_features(sobj, min_cells = 5)
## [1] 20003  3416
## [1] 16683  3416
sobj
## An object of class Seurat 
## 16683 features across 3416 samples within 1 assay 
## Active assay: RNA (16683 features, 2837 variable features)

We normalize the count matrix for remaining cells :

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize")
sobj = Seurat::FindVariableFeatures(sobj, nfeatures = 2000)
sobj = Seurat::ScaleData(sobj)

sobj
## An object of class Seurat 
## 16683 features across 3416 samples within 1 assay 
## Active assay: RNA (16683 features, 2000 variable features)

We perform a PCA :

sobj = Seurat::RunPCA(sobj,
                      assay = "RNA",
                      reduction.name = "RNA_pca",
                      npcs = 100,
                      seed.use = 1337L)
sobj
## An object of class Seurat 
## 16683 features across 3416 samples within 1 assay 
## Active assay: RNA (16683 features, 2000 variable features)
##  1 dimensional reduction calculated: RNA_pca

We choose the number of dimensions such that they summarize 35 % of the variability :

stdev = sobj@reductions[["RNA_pca"]]@stdev
stdev_prop = cumsum(stdev)/sum(stdev)
ndims = which(stdev_prop > 0.35)[1]
ndims
## [1] 20

We can visualize this on the elbow plot :

elbow_p = Seurat::ElbowPlot(sobj, ndims = 100, reduction = "RNA_pca") +
  ggplot2::geom_point(x = ndims, y = stdev[ndims], col = "red")
x_text = ggplot_build(elbow_p)$layout$panel_params[[1]]$x$get_labels() %>% as.numeric()
elbow_p = elbow_p +
  ggplot2::scale_x_continuous(breaks = sort(c(x_text, ndims)), limits = c(0, 100))
x_color = ifelse(ggplot_build(elbow_p)$layout$panel_params[[1]]$x$get_labels() %>%
                   as.numeric() %>% round(., 2) == round(ndims, 2), "red", "black")
elbow_p = elbow_p +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = element_text(color = x_color))

elbow_p

Without correction

We generate a tSNE and a UMAP with 20 principal components :

sobj = Seurat::RunTSNE(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_tsne"))

sobj = Seurat::RunUMAP(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_umap"))

We can visualize the two representations :

tsne = Seurat::DimPlot(sobj, group.by = "project_name",
                       reduction = paste0("RNA_pca_", ndims, "_tsne")) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  Seurat::NoAxes() + ggplot2::ggtitle("PCA - tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "project_name",
                       reduction = paste0("RNA_pca_", ndims, "_umap")) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  Seurat::NoAxes() + ggplot2::ggtitle("PCA - UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

There is a batch-effect mainly in the right population : due to the disease ?

Harmony

We remove batch-effect using Harmony :

`%||%` = function(lhs, rhs) {
  if (!is.null(x = lhs)) {
    return(lhs)
  } else {
    return(rhs)
  }
}

set.seed(1337L)
sobj = harmony::RunHarmony(object = sobj,
                           group.by.vars = "project_name",
                           plot_convergence = TRUE,
                           reduction = "RNA_pca",
                           assay.use = "RNA",
                           reduction.save = "harmony",
                           max.iter.harmony = 20,
                           project.dim = FALSE)

From this batch-effect removed projection, we generate a tSNE and a UMAP.

sobj = Seurat::RunUMAP(sobj, 
                       seed.use = 1337L,
                       dims = 1:ndims,
                       reduction = "harmony",
                       reduction.name = paste0("harmony_", ndims, "_umap"),
                       reduction.key = paste0("harmony_", ndims, "umap_"))
sobj = Seurat::RunTSNE(sobj,
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction = "harmony",
                       reduction.name = paste0("harmony_", ndims, "_tsne"),
                       reduction.key = paste0("harmony", ndims, "tsne_"))

These are the corrected UMAP and tSNE :

tsne = Seurat::DimPlot(sobj, group.by = "project_name",
                       reduction = paste0("harmony_", ndims, "_tsne")) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  Seurat::NoAxes() + ggplot2::ggtitle("PCA - harmony - tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "project_name",
                       reduction = paste0("harmony_", ndims, "_umap")) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  Seurat::NoAxes() + ggplot2::ggtitle("PCA - harmony - UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

We will keep the tSNE from Harmony :

reduction = "harmony"
name2D = paste0("harmony_", ndims, "_tsne")

Clustering

We generate a clustering :

sobj = Seurat::FindNeighbors(sobj, reduction = reduction, dims = 1:ndims)
sobj = Seurat::FindClusters(sobj, resolution = 1)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 3416
## Number of edges: 133015
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7801
## Number of communities: 12
## Elapsed time: 0 seconds
dimplot_clusters = Seurat::DimPlot(sobj, reduction = name2D, label = TRUE) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1)
dimplot_clusters

Visualization

We can represent the 4 quality metrics :

plot_list = Seurat::FeaturePlot(sobj, reduction = name2D,
                                combine = FALSE, pt.size = 0.5,
                                features = c("percent.mt", "percent.rb", "nFeature_RNA", "log_nCount_RNA"))
plot_list = lapply(plot_list, FUN = function(one_plot) {
  one_plot +
    Seurat::NoAxes() +
    ggplot2::scale_color_gradientn(colors = aquarius:::color_gene) +
    ggplot2::theme(aspect.ratio = 1)
})

patchwork::wrap_plots(plot_list, nrow = 1)

Project name

We can visualize the two batch-effect corrected representations :

plot_list = lapply(c(paste0("harmony_", ndims, "_tsne"),
                     paste0("harmony_", ndims, "_umap")), FUN = function(one_proj) {
                       Seurat::DimPlot(sobj, group.by = "project_name",
                                       reduction = one_proj) +
                         ggplot2::scale_color_manual(values = sample_info$color,
                                                     breaks = sample_info$project_name) +
                         Seurat::NoAxes() + ggplot2::ggtitle(one_proj) +
                         ggplot2::theme(aspect.ratio = 1,
                                        plot.title = element_text(hjust = 0.5),
                                        legend.position = "none")
                     })

patchwork::wrap_plots(plot_list, ncol = 2)

Cell type

We also visualize cell types :

plot_list = lapply(c(paste0("harmony_", ndims, "_tsne"),
                     paste0("harmony_", ndims, "_umap")), FUN = function(one_proj) {
                       Seurat::DimPlot(sobj, group.by = "cell_type",
                                       reduction = one_proj) +
                         ggplot2::scale_color_manual(values = color_markers,
                                                     breaks = names(color_markers)) +
                         Seurat::NoAxes() + ggplot2::ggtitle(one_proj) +
                         ggplot2::theme(aspect.ratio = 1,
                                        plot.title = element_text(hjust = 0.5),
                                        legend.position = "none")
                     })

patchwork::wrap_plots(plot_list, ncol = 2)

Clusters

We can represent clusters, split by sample of origin :

plot_list = aquarius::plot_split_dimred(sobj,
                                        reduction = name2D,
                                        split_by = "sample_identifier",
                                        group_by = "seurat_clusters",
                                        split_color = setNames(sample_info$color,
                                                               nm = sample_info$sample_identifier),
                                        group_color = aquarius::gg_color_hue(length(levels(sobj$seurat_clusters))),
                                        main_pt_size = 0.5, bg_pt_size = 0.5)

plot_list[[length(plot_list) + 1]] = dimplot_clusters

patchwork::wrap_plots(plot_list, ncol = 4) +
  patchwork::plot_layout(guides = "collect") &
  ggplot2::theme(legend.position = "none")

We make a heatmap to see clusters distribution among samples :

cluster_markers = c("KRT14",
                    # IFE basal-related
                    "KRT16", "MGP", "KRT6C", "CST6",
                    # IFE basal
                    "GPX2", "C1QTNF12", "PTN", "CLEC2B", "TGFBI",
                    # QC metrics
                    "TOP2A", "MCM5",
                    "percent.mt", "percent.rb", "log_nCount_RNA")

ht_annot = Seurat::FetchData(sobj, slot = "data", vars = cluster_markers) %>%
  as.data.frame()
ht_annot$clusters = sobj$seurat_clusters
ht_annot = ht_annot %>%
  dplyr::group_by(clusters) %>%
  dplyr::summarise_all(funs('mean' = mean)) %>%
  as.data.frame() %>%
  dplyr::select(-clusters) %>%
  `colnames<-`(c(cluster_markers))
head(ht_annot)
##      KRT14      KRT16         MGP      KRT6C       CST6       GPX2   C1QTNF12
## 1 4.594290 0.06099838 0.006447587 0.02332942 0.02215209 0.95327612 1.08262394
## 2 5.822020 4.55832660 0.105068361 2.11952477 1.00988691 0.06301549 0.09281149
## 3 4.542902 0.06463788 0.020509349 0.02724677 0.02977043 0.68825415 1.13116006
## 4 4.643753 0.05179121 0.001583540 0.01026374 0.01917485 0.70179295 0.55828331
## 5 5.020896 0.11226035 0.012526752 0.01021032 0.02190074 0.33410133 0.15685593
## 6 4.973422 1.63007168 1.777878336 0.21746173 0.17254779 0.03252731 0.01617996
##           PTN      CLEC2B      TGFBI       TOP2A       MCM5 percent.mt
## 1 1.155960932 0.362149066 0.14961623 0.018487882 0.14410646  5.8876590
## 2 0.096835341 0.004336307 0.00000000 0.004419252 0.01873488  0.2179762
## 3 1.683696801 0.088212797 0.08970869 0.017327792 0.11218501  5.8619901
## 4 0.270831349 0.726225494 0.52769949 0.013035064 0.08841249  6.3534252
## 5 0.003856703 1.001295689 0.63708980 0.006790053 0.08129976  7.8340236
## 6 0.167698275 0.013468035 0.02512972 0.051624334 0.14729860  3.3225015
##   percent.rb log_nCount_RNA
## 1   30.57707       9.476068
## 2   24.84052       7.802923
## 3   27.67438       9.311167
## 4   30.87513       9.257627
## 5   26.55977       9.587476
## 6   23.74584      10.040056
color_fun = function(one_gene) {
  gene_range = range(ht_annot[, one_gene])
  gene_palette = circlize::colorRamp2(colors = c("#FFFFFF", aquarius::color_gene[-1]),
                                      breaks = seq(from = gene_range[1], to = gene_range[2],
                                                   length.out = length(aquarius::color_gene)))
  return(gene_palette)
}

ha = ComplexHeatmap::HeatmapAnnotation(df = ht_annot,
                                       which = "column",
                                       show_legend = TRUE,
                                       col = setNames(nm = cluster_markers,
                                                      lapply(cluster_markers, FUN = color_fun)),
                                       annotation_name_side = "left")

ht = aquarius::plot_prop_heatmap(df = sobj@meta.data[, c("sample_identifier", "seurat_clusters")],
                                 bottom_annotation = ha,
                                 cluster_rows = TRUE,
                                 prop_margin = 1,
                                 row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                           col = sample_info$color,
                                                           fontface = "bold"),
                                 row_title = "Sample",
                                 column_title = "Cluster")

ComplexHeatmap::draw(ht,
                     merge_legends = TRUE)

We also look at genes of interest on the projection :

plot_list = lapply(cluster_markers, FUN = function(one_gene) {
  p = Seurat::FeaturePlot(sobj, features = one_gene,
                          pt.size = 0.2, reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes() +
    ggplot2::theme(aspect.ratio = 1)
  
  return(p)
})

patchwork::wrap_plots(plot_list, ncol = 5)

Save

We save the Seurat object :

saveRDS(sobj, file = paste0(out_dir, "/", save_name, "_sobj.rds"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggplot2_3.3.5   patchwork_1.1.2 dplyr_1.0.7    
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              parallel_3.6.3             
##  [33] scater_1.14.6               irlba_2.3.3                
##  [35] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [37] Rcpp_1.0.9                  readr_2.0.2                
##  [39] KernSmooth_2.23-17          carrier_0.1.0              
##  [41] promises_1.1.0              gdata_2.18.0               
##  [43] DelayedArray_0.12.3         limma_3.42.2               
##  [45] graph_1.64.0                RcppParallel_5.1.4         
##  [47] Hmisc_4.4-0                 fs_1.5.2                   
##  [49] RSpectra_0.16-0             fastmatch_1.1-0            
##  [51] ranger_0.12.1               digest_0.6.25              
##  [53] png_0.1-7                   sctransform_0.2.1          
##  [55] cowplot_1.0.0               DOSE_3.12.0                
##  [57] here_1.0.1                  TInGa_0.0.0.9000           
##  [59] ggraph_2.0.3                pkgconfig_2.0.3            
##  [61] GO.db_3.10.0                DelayedMatrixStats_1.8.0   
##  [63] gower_0.2.1                 ggbeeswarm_0.6.0           
##  [65] iterators_1.0.12            DropletUtils_1.6.1         
##  [67] reticulate_1.26             clusterProfiler_3.14.3     
##  [69] SummarizedExperiment_1.16.1 circlize_0.4.15            
##  [71] beeswarm_0.4.0              GetoptLong_1.0.5           
##  [73] xfun_0.35                   bslib_0.3.1                
##  [75] zoo_1.8-10                  tidyselect_1.1.0           
##  [77] reshape2_1.4.4              purrr_0.3.4                
##  [79] ica_1.0-2                   pcaPP_1.9-73               
##  [81] viridisLite_0.3.0           rtracklayer_1.46.0         
##  [83] rlang_1.0.2                 hexbin_1.28.1              
##  [85] jquerylib_0.1.4             dyneval_0.9.9              
##  [87] glue_1.4.2                  RColorBrewer_1.1-2         
##  [89] matrixStats_0.56.0          stringr_1.4.0              
##  [91] lava_1.6.7                  europepmc_0.3              
##  [93] DESeq2_1.26.0               recipes_0.1.17             
##  [95] labeling_0.3                harmony_0.1.0              
##  [97] httpuv_1.5.2                class_7.3-17               
##  [99] BiocNeighbors_1.4.2         DO.db_2.9                  
## [101] annotate_1.64.0             jsonlite_1.7.2             
## [103] XVector_0.26.0              bit_4.0.4                  
## [105] mime_0.9                    aquarius_0.1.5             
## [107] Rsamtools_2.2.3             gridExtra_2.3              
## [109] gplots_3.0.3                stringi_1.4.6              
## [111] processx_3.5.2              gsl_2.1-6                  
## [113] bitops_1.0-6                cli_3.0.1                  
## [115] batchelor_1.2.4             RSQLite_2.2.0              
## [117] randomForest_4.6-14         tidyr_1.1.4                
## [119] data.table_1.14.2           rstudioapi_0.13            
## [121] org.Mm.eg.db_3.10.0         GenomicAlignments_1.22.1   
## [123] nlme_3.1-147                qvalue_2.18.0              
## [125] scran_1.14.6                locfit_1.5-9.4             
## [127] scDblFinder_1.1.8           listenv_0.8.0              
## [129] ggthemes_4.2.4              gridGraphics_0.5-0         
## [131] R.oo_1.24.0                 dbplyr_1.4.4               
## [133] BiocGenerics_0.32.0         TTR_0.24.2                 
## [135] readxl_1.3.1                lifecycle_1.0.1            
## [137] timeDate_3043.102           ggpattern_0.3.1            
## [139] munsell_0.5.0               cellranger_1.1.0           
## [141] R.methodsS3_1.8.1           proxyC_0.1.5               
## [143] visNetwork_2.0.9            caTools_1.18.0             
## [145] codetools_0.2-16            Biobase_2.46.0             
## [147] GenomeInfoDb_1.22.1         vipor_0.4.5                
## [149] lmtest_0.9-38               msigdbr_7.5.1              
## [151] htmlTable_1.13.3            triebeard_0.3.0            
## [153] lsei_1.2-0                  xtable_1.8-4               
## [155] ROCR_1.0-7                  BiocManager_1.30.10        
## [157] scatterplot3d_0.3-41        abind_1.4-5                
## [159] farver_2.0.3                parallelly_1.28.1          
## [161] RANN_2.6.1                  askpass_1.1                
## [163] GenomicRanges_1.38.0        RcppAnnoy_0.0.16           
## [165] tibble_3.1.5                ggdendro_0.1-20            
## [167] cluster_2.1.0               future.apply_1.5.0         
## [169] Seurat_3.1.5                dendextend_1.15.1          
## [171] Matrix_1.3-2                ellipsis_0.3.2             
## [173] prettyunits_1.1.1           lubridate_1.7.9            
## [175] ggridges_0.5.2              igraph_1.2.5               
## [177] RcppEigen_0.3.3.7.0         fgsea_1.12.0               
## [179] remotes_2.4.2               scBFA_1.0.0                
## [181] destiny_3.0.1               VIM_6.1.1                  
## [183] testthat_3.1.0              htmltools_0.5.2            
## [185] BiocFileCache_1.10.2        yaml_2.2.1                 
## [187] utf8_1.1.4                  plotly_4.9.2.1             
## [189] XML_3.99-0.3                ModelMetrics_1.2.2.2       
## [191] e1071_1.7-3                 foreign_0.8-76             
## [193] withr_2.5.0                 fitdistrplus_1.0-14        
## [195] BiocParallel_1.20.1         xgboost_1.4.1.1            
## [197] bit64_4.0.5                 foreach_1.5.0              
## [199] robustbase_0.93-9           Biostrings_2.54.0          
## [201] GOSemSim_2.13.1             rsvd_1.0.3                 
## [203] memoise_2.0.0               evaluate_0.18              
## [205] forcats_0.5.0               rio_0.5.16                 
## [207] geneplotter_1.64.0          tzdb_0.1.2                 
## [209] caret_6.0-86                ps_1.6.0                   
## [211] DiagrammeR_1.0.6.1          curl_4.3                   
## [213] fdrtool_1.2.15              fansi_0.4.1                
## [215] highr_0.8                   urltools_1.7.3             
## [217] xts_0.12.1                  GSEABase_1.48.0            
## [219] acepack_1.4.1               edgeR_3.28.1               
## [221] checkmate_2.0.0             scds_1.2.0                 
## [223] cachem_1.0.6                npsurv_0.4-0               
## [225] babelgene_22.3              rjson_0.2.20               
## [227] openxlsx_4.1.5              ggrepel_0.9.1              
## [229] clue_0.3-60                 rprojroot_2.0.2            
## [231] stabledist_0.7-1            tools_3.6.3                
## [233] sass_0.4.0                  nichenetr_1.1.1            
## [235] magrittr_2.0.1              RCurl_1.98-1.2             
## [237] proxy_0.4-24                car_3.0-11                 
## [239] ape_5.3                     ggplotify_0.0.5            
## [241] xml2_1.3.2                  httr_1.4.2                 
## [243] assertthat_0.2.1            rmarkdown_2.18             
## [245] boot_1.3-25                 globals_0.14.0             
## [247] R6_2.4.1                    Rhdf5lib_1.8.0             
## [249] nnet_7.3-14                 RcppHNSW_0.2.0             
## [251] progress_1.2.2              genefilter_1.68.0          
## [253] statmod_1.4.34              gtools_3.8.2               
## [255] shape_1.4.6                 HDF5Array_1.14.4           
## [257] BiocSingular_1.2.2          rhdf5_2.30.1               
## [259] splines_3.6.3               AUCell_1.8.0               
## [261] carData_3.0-4               colorspace_1.4-1           
## [263] generics_0.1.0              stats4_3.6.3               
## [265] base64enc_0.1-3             dynfeature_1.0.0           
## [267] smoother_1.1                gridtext_0.1.1             
## [269] pillar_1.6.3                tweenr_1.0.1               
## [271] sp_1.4-1                    ggplot.multistats_1.0.0    
## [273] rvcheck_0.1.8               GenomeInfoDbData_1.2.2     
## [275] plyr_1.8.6                  gtable_0.3.0               
## [277] zip_2.2.0                   knitr_1.41                 
## [279] ComplexHeatmap_2.14.0       latticeExtra_0.6-29        
## [281] biomaRt_2.42.1              IRanges_2.20.2             
## [283] fastmap_1.1.0               ADGofTest_0.3              
## [285] copula_1.0-0                doParallel_1.0.15          
## [287] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] S4Vectors_0.24.4            ipred_0.9-12               
## [295] enrichplot_1.6.1            hms_1.1.1                  
## [297] ggforce_0.3.1               Rtsne_0.15                 
## [299] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             grid_3.6.3                 
## [303] lazyeval_0.2.2              Formula_1.2-3              
## [305] tsne_0.1-3                  crayon_1.3.4               
## [307] MASS_7.3-54                 pROC_1.16.2                
## [309] viridis_0.5.1               dynparam_1.0.0             
## [311] rpart_4.1-15                zinbwave_1.8.0             
## [313] compiler_3.6.3              ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJab29tIGluIE9SUyBhbmQgSUZFIGJhc2FsIGNlbGxzIgphdXRob3I6ICJBdWRyZXkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVZLSVtLSVkJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKLS0tCgo8c3R5bGU+CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQo8L3N0eWxlPgoKPCEtLSBBdXRvbWF0aWNhbGx5IGNvbXB1dGVzIGFuZCBwcmludHMgaW4gdGhlIG91dHB1dCB0aGUgcnVubmluZyB0aW1lIGZvciBhbnkgY29kZSBjaHVuayAtLT4KYGBge3IsIGVjaG89RkFMU0V9CiMgaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duL2lzc3Vlcy8xNDUzCmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkXyIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT4iLCAic2hvdyIsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKSwKICB0aW1lX2l0ID0gbG9jYWwoewogICAgbm93ID0gTlVMTAogICAgZnVuY3Rpb24oYmVmb3JlLCBvcHRpb25zKSB7CiAgICAgIGlmIChvcHRpb25zJHRpbWVfaXQpIHsKICAgICAgICBpZiAoYmVmb3JlKSB7CiAgICAgICAgICBub3cgPD0gU3lzLnRpbWUoKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXMgPSBkaWZmdGltZShTeXMudGltZSgpLCBub3csIHVuaXRzID0gInNlY3MiKQogICAgICAgICAgcGFzdGUoIihUaW1lIHRvIHJ1biA6Iiwgcm91bmQocmVzLCBkaWdpdHMgPSAyKSwgInMpIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KQopCmBgYAoKPCEtLSBTZXQgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBhbGwgY2h1bmtzIC0tPgpgYGB7ciwgc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc2V0LnNlZWQoMTMzN0wpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgIyBkaXNwbGF5IGNvZGUKICAgICAgICAgICAgICAgICAgICAgICMgZGlzcGxheSBjaHVuayBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfb3V0cHV0ID0gRkFMU0UsICMgdXNlZnVsbCBmb3Igc2Vzc2lvbkluZm8oKQogICAgICAgICAgICAgICAgICAgICAgZm9sZF9wbG90ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgZmlndXJlIHNldHRpbmdzCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAnY2VudGVyJywKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDIwLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDE1LAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIHNvbWV0aGluZyBhYm91dCBzZWVkLCBjaHVuayBhbmQgUm1hcmtkb3duIGNvbXBpbGF0aW9uCiAgICAgICAgICAgICAgICAgICAgICAjIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzM5NDE3MDAzL2xvbmctdmVjdG9ycy1ub3Qtc3VwcG9ydGVkLXlldC1lcnJvci1pbi1ybWQtYnV0LW5vdC1pbi1yLXNjcmlwdAogICAgICAgICAgICAgICAgICAgICAgIyBjYWNoZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBjYWNoZS5sYXp5ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIGFkZCBydW50aW1lIGFmdGVyIGNodW5rCiAgICAgICAgICAgICAgICAgICAgICB0aW1lX2l0ID0gRkFMU0UpCmBgYAoKClRoaXMgZmlsZSBpcyB1c2VkIHRvIGdlbmVyYXRlIGEgZGF0YXNldCBjb250YWluaW5nIG9ubHkgT1JTIGFuZCBJRkUgYmFzYWwgY2VsbHMuCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3Bsb3QyKQoKLmxpYlBhdGhzKCkKYGBgCgoKIyBQcmVwYXJhdGlvbgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBzZXQgdGhlIGdsb2JhbCBzZXR0aW5ncyBvZiB0aGUgYW5hbHlzaXMuIFdlIHdpbGwgc3RvcmUgZGF0YSB0aGVyZSA6CgpgYGB7ciBvdXRfZGlyfQpzYXZlX25hbWUgPSAib3JzX2lmZWIiCm91dF9kaXIgPSAiLiIKYGBgCgpXZSBsb2FkIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gOgoKYGBge3IgY3VzdG9tX3BhbGV0dGVfc2FtcGxlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0Kc2FtcGxlX2luZm8gPSByZWFkUkRTKHBhc3RlMChvdXRfZGlyLCAiLy4uLy4uLzFfbWV0YWRhdGEvaHNfaGRfc2FtcGxlX2luZm8ucmRzIikpCnByb2plY3RfbmFtZXNfb2kgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUKCmdyYXBoaWNzOjpwaWUocmVwKDEsIG5yb3coc2FtcGxlX2luZm8pKSwKICAgICAgICAgICAgICBjb2wgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICBsYWJlbHMgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpCmBgYAoKSGVyZSBhcmUgY3VzdG9tIGNvbG9ycyBmb3IgZWFjaCBjZWxsIHR5cGUgOgoKYGBge3IgY29sb3JfbWFya2VycywgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxLjIsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8uLi8xX21ldGFkYXRhL2hzX2hkX2NvbG9yX21hcmtlcnMucmRzIikpCgpkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgIGNvbG9yID0gdW5saXN0KGNvbG9yX21hcmtlcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpLCBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSkKYGBgCgojIE1ha2UgYHIgc2F2ZV9uYW1lYCBkYXRhc2V0CgojIyBBdGxhcwoKV2UgbG9hZCB0aGUgY29tYmluZWQgZGF0YXNldCBjb250YWluaW5nIGFsbCBjZWxsIHR5cGVzIGZyb20gYWxsIHNhbXBsZXMgOgoKYGBge3IgbG9hZF9hdGxhc30Kc29iaiA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vLi4vM19jb21iaW5lZC9oc19oZF9zb2JqLnJkcyIpKQpzb2JqCmBgYAoKV2UgcmVwcmVzZW50IGNlbGxzIGluIHRoZSB0U05FIDoKCmBgYHtyIG5hbWUyRH0KbmFtZTJEID0gImhhcm1vbnlfMzhfdHNuZSIKYGBgCgpXZSBzbW9vdGggY2VsbCB0eXBlIGFubm90YXRpb24gYXQgYSBjbHVzdGVyIGxldmVsIDoKCmBgYHtyIHNtb290aF9hbm5vdGF0aW9ufQpjbHVzdGVyX3R5cGUgPSB0YWJsZShzb2JqJGNlbGxfdHlwZSwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqJGNlbGxfdHlwZSlbY2x1c3Rlcl90eXBlXSkKCnNvYmokY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmokc2V1cmF0X2NsdXN0ZXJzXQpgYGAKCgpXZSBsb29rIGdlbmUgbWFya2VycyBleHByZXNzaW9uIGxldmVsLCBjZWxsIGFubm90YXRpb24gYW5kIGNsdXN0ZXItc21vb3RoZWQgYW5ub3RhdGlvbiBvbiB0aGUgcHJvamVjdGlvbiwgdG8gbG9jYXRlIGByIHNhdmVfbmFtZWAgY2VsbHMgOgoKYGBge3Igc2VlX29yc19pZmViX21hcmtlcnMsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gM30Kb3JzX2lmZWJfbWFya2VycyA9IGMoIktSVDE2IiwgIkVIRiIsICJBTERIM0ExIikKb3JzX2lmZWJfY2VsbF90eXBlID0gYygiT1JTIiwgIklGRSBiYXNhbCIpCmNvbG9yX21hcmtlcnNbIShuYW1lcyhjb2xvcl9tYXJrZXJzKSAlaW4lIG9yc19pZmViX2NlbGxfdHlwZSldID0gImdyYXk5MiIKCiMgRmVhdHVyZSBQbG90CnBsb3RfbGlzdCA9IGxhcHBseShvcnNfaWZlYl9tYXJrZXJzLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IG9uZV9nZW5lKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6OmNvbG9yX2dlbmUpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKICByZXR1cm4ocCkKfSkKCiMgQ2VsbCB0eXBlIGFubm90YXRpb24KcGxvdF9saXN0W1tsZW5ndGgocGxvdF9saXN0KSArIDFdXSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSBjb2xvcl9tYXJrZXJzLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBzYXZlX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkNlbGwgYW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChzdW0oc29iaiRjZWxsX3R5cGUgJWluJSBvcnNfaWZlYl9jZWxsX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiBjZWxscyIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCiMgQ2x1c3Rlci1zbW9vdGhlZCBhbm5vdGF0aW9uCnBsb3RfbGlzdFtbbGVuZ3RoKHBsb3RfbGlzdCkgKyAxXV0gPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHVubmFtZSh1bmxpc3QoY29sb3JfbWFya2Vyc1tvcnNfaWZlYl9jZWxsX3R5cGVdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJncmF5OTIiLCBsZW5ndGgoY29sb3JfbWFya2VycykgLSBsZW5ndGgob3JzX2lmZWJfY2VsbF90eXBlKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKG9yc19pZmViX2NlbGxfdHlwZSwgc2V0ZGlmZihuYW1lcyhjb2xvcl9tYXJrZXJzKSwgb3JzX2lmZWJfY2VsbF90eXBlKSkpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkNsdXN0ZXIgYW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChzdW0oc29iaiRjbHVzdGVyX3R5cGUgJWluJSBvcnNfaWZlYl9jZWxsX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiBjZWxscyIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5yb3cgPSAxKQpgYGAKCkR1ZSB0byBjbHVzdGVyaW5nIHRoYXQgYmFkbHkgc2VwYXJhdGUgT1JTIGZyb20gSUZFLCB0aGVyZSBpcyBhIG1pcy1zZWxlY3Rpb24gb2YgY2VsbHMgb2YgaW50ZXJlc3QgOiBmYWxzZSBwb3NpdGl2ZS4gSG93ZXZlciwgYW5ub3RhdGlvbiBzbW9vdGhpbmcgYWRkcyBhIGxvdCBvZiAoYWxsKSBmYWxzZSBuZWdhdGl2ZSBhbmQgY2xlYW4gdGhlIGFubm90YXRpb24uIFdlIGV4dHJhY3QgY2VsbHMgb2YgaW50ZXJlc3QgYmFzZWQgb24gY2x1c3RlcmluZy4gVGhlbiwgd2UgZ2VuZXJhdGUgYSBuZXcgY2x1c3RlciB0byByZW1vdmUgZmFsc2UgcG9zaXRpdmUuCgojIyBDb21iaW5lZCBkYXRhc2V0CgpXZSBleHRyYWN0IGNlbGxzIG9mIGludGVyZXN0IGJhc2VkIG9uIHRoZSBjbHVzdGVyaW5nIDoKCmBgYHtyIHN1YnNldF9pc19vZl9pbnRlcmVzdH0Kc29iaiA9IHN1YnNldChzb2JqLCBjbHVzdGVyX3R5cGUgJWluJSBvcnNfaWZlYl9jZWxsX3R5cGUpCnNvYmoKYGBgCgpXZSByZW1vdmUgYWxsIHRoaW5ncyB0aGF0IHdlcmUgY2FsY3VsYXRlZCBiYXNlZCBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciByZW1vdmVfcmVkdWN0aW9uc30Kc29iaiA9IFNldXJhdDo6RGlldFNldXJhdChzb2JqKQpzb2JqCmBgYAoKIyMgQ2xlYW4gbWV0YWRhdGEKCldlIGtlZXAgYSBzdWJzZXQgb2YgbWV0YS5kYXRhIGFuZCByZXNldCBsZXZlbHMgOgoKYGBge3Igc29ial9zZXRfZmFjdG9yX2xldmVsc30Kc29iakBtZXRhLmRhdGEgPSBzb2JqQG1ldGEuZGF0YVssIGMoIm9yaWcuaWRlbnQiLCAibkNvdW50X1JOQSIsICJuRmVhdHVyZV9STkEiLCAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJvamVjdF9uYW1lIiwgInNhbXBsZV9pZGVudGlmaWVyIiwgInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhYm9yYXRvcnkiLCAibG9jYXRpb24iLCAiU2V1cmF0LlBoYXNlIiwgImN5Y2xvbmUuUGhhc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVyY2VudC5tdCIsICJwZXJjZW50LnJiIiwgImNlbGxfdHlwZSIpXQoKc29iaiRvcmlnLmlkZW50ID0gZmFjdG9yKHNvYmokb3JpZy5pZGVudCwgbGV2ZWxzID0gbGV2ZWxzKHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkpCnNvYmokcHJvamVjdF9uYW1lID0gZmFjdG9yKHNvYmokcHJvamVjdF9uYW1lLCBsZXZlbHMgPSBsZXZlbHMoc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKSkKc29iaiRzYW1wbGVfaWRlbnRpZmllciA9IGZhY3Rvcihzb2JqJHNhbXBsZV9pZGVudGlmaWVyLCBsZXZlbHMgPSBsZXZlbHMoc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIpKQpzb2JqJHNhbXBsZV90eXBlID0gZmFjdG9yKHNvYmokc2FtcGxlX3R5cGUsIGxldmVscyA9IGxldmVscyhzYW1wbGVfaW5mbyRzYW1wbGVfdHlwZSkpCgpzdW1tYXJ5KHNvYmpAbWV0YS5kYXRhKQpgYGAKCgojIFByb2Nlc3NpbmcKCiMjIE1ldGFkYXRhCgpIb3cgbWFueSBjZWxscyBieSBzYW1wbGUgPwoKYGBge3IgdGFibGVfb3JpZ19pZGVudH0KdGFibGUoc29iaiRwcm9qZWN0X25hbWUpCmBgYAoKV2UgcmVwcmVzZW50IHRoaXMgaW5mb3JtYXRpb24gYXMgYSBiYXJwbG90IDoKCmBgYHtyIGJhcnBsb3RfY291bnQsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQphcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gdGFibGUoc29iaiRwcm9qZWN0X25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGNlbGxfdHlwZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBgY29sbmFtZXM8LWAoYygicHJvamVjdF9uYW1lIiwgImNlbGxfdHlwZSIsICJuYl9jZWxscyIpKSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gInByb2plY3RfbmFtZSIsIHkgPSAibmJfY2VsbHMiLCBmaWxsID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjaygpKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgdHlwZSIpCmBgYAoKIyMgUmVtb3ZlIGZhbHNlIHBvc2l0aXZlCgpXZSBub3JtYWxpemUgZ2VuZSBleHByZXNzaW9uIGZvciByZW1haW5pbmcgY2VsbHMgOgoKYGBge3Igbm9ybWFsaXphdGlvbn0Kc29iaiA9IFNldXJhdDo6Tm9ybWFsaXplRGF0YShzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIpCnNvYmogPSBTZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzKHNvYmosIG5mZWF0dXJlcyA9IDMwMDApCnNvYmogPSBTZXVyYXQ6OlNjYWxlRGF0YShzb2JqKQoKc29iagpgYGAKCldlIHBlcmZvcm0gYSBQQ0EgOgoKYGBge3IgcGNhfQpzb2JqID0gU2V1cmF0OjpSdW5QQ0Eoc29iaiwKICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9ICJSTkFfcGNhIiwKICAgICAgICAgICAgICAgICAgICAgIG5wY3MgPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMKQpzb2JqCmBgYAoKV2UgY2hvb3NlIHRoZSBudW1iZXIgb2YgZGltZW5zaW9ucyBzdWNoIHRoYXQgdGhleSBzdW1tYXJpemUgNjAgJSBvZiB0aGUgdmFyaWFiaWxpdHkgOgoKYGBge3IgbmRpbXN9CnN0ZGV2ID0gc29iakByZWR1Y3Rpb25zW1siUk5BX3BjYSJdXUBzdGRldgpzdGRldl9wcm9wID0gY3Vtc3VtKHN0ZGV2KS9zdW0oc3RkZXYpCm5kaW1zID0gd2hpY2goc3RkZXZfcHJvcCA+IDAuNjApWzFdCm5kaW1zCmBgYAoKV2UgY2FuIHZpc3VhbGl6ZSB0aGlzIG9uIHRoZSBlbGJvdyBwbG90IDoKCmBgYHtyIGVsYm93cGxvdCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0fQplbGJvd19wID0gU2V1cmF0OjpFbGJvd1Bsb3Qoc29iaiwgbmRpbXMgPSAxMDAsIHJlZHVjdGlvbiA9ICJSTkFfcGNhIikgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoeCA9IG5kaW1zLCB5ID0gc3RkZXZbbmRpbXNdLCBjb2wgPSAicmVkIikKeF90ZXh0ID0gZ2dwbG90X2J1aWxkKGVsYm93X3ApJGxheW91dCRwYW5lbF9wYXJhbXNbWzFdXSR4JGdldF9sYWJlbHMoKSAlPiUgYXMubnVtZXJpYygpCmVsYm93X3AgPSBlbGJvd19wICsKICBnZ3Bsb3QyOjpzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc29ydChjKHhfdGV4dCwgbmRpbXMpKSwgbGltaXRzID0gYygwLCAxMDApKQp4X2NvbG9yID0gaWZlbHNlKGdncGxvdF9idWlsZChlbGJvd19wKSRsYXlvdXQkcGFuZWxfcGFyYW1zW1sxXV0keCRnZXRfbGFiZWxzKCkgJT4lCiAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKCkgJT4lIHJvdW5kKC4sIDIpID09IHJvdW5kKG5kaW1zLCAyKSwgInJlZCIsICJibGFjayIpCmVsYm93X3AgPSBlbGJvd19wICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0geF9jb2xvcikpCgplbGJvd19wCmBgYAoKV2UgZ2VuZXJhdGUgYSB0U05FIGFuZCBhIFVNQVAgd2l0aCBgciBuZGltc2AgcHJpbmNpcGFsIGNvbXBvbmVudHMgOgoKYGBge3IgdHNuZV91bWFwfQpzb2JqID0gU2V1cmF0OjpSdW5UU05FKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gIlJOQV9wY2EiLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm5kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMTMzN0wsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLm5hbWUgPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdHNuZSIpKQoKc29iaiA9IFNldXJhdDo6UnVuVU1BUChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJSTkFfcGNhIiwKICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMTpuZGltcywKICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi5uYW1lID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3VtYXAiKSkKYGBgCgpXZSBjYW4gdmlzdWFsaXplIHRoZSB0d28gcmVwcmVzZW50YXRpb25zIDoKCmBgYHtyIHNlZV91bWFwX3RzbmUsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KdHNuZSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJwcm9qZWN0X25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IHBhc3RlMCgiUk5BX3BjYV8iLCBuZGltcywgIl90c25lIikpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJQQ0EgLSB0U05FIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKdW1hcCA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJwcm9qZWN0X25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IHBhc3RlMCgiUk5BX3BjYV8iLCBuZGltcywgIl91bWFwIikpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJQQ0EgLSBVTUFQIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgp0c25lIHwgdW1hcApgYGAKCgpXZSBnZW5lcmF0ZSBhIGNsdXN0ZXJpbmcgZnJvbSB0aGUgUENBIDoKCmBgYHtyIGNsdXN0ZXJpbmcsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA1fQpyZWR1Y3Rpb25fbmFtZSA9ICJSTkFfcGNhIgoKc29iaiA9IFNldXJhdDo6RmluZE5laWdoYm9ycyhzb2JqLCByZWR1Y3Rpb24gPSByZWR1Y3Rpb25fbmFtZSkKc29iaiA9IFNldXJhdDo6RmluZENsdXN0ZXJzKHNvYmosIHJlc29sdXRpb24gPSAxLjcpCgpjbHVzdGVyX3Bsb3QgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgbGFiZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKHJlZHVjdGlvbl9uYW1lLCAiXyIsIG5kaW1zLCAiX3VtYXAiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmNsdXN0ZXJfcGxvdApgYGAKCldlIGxvb2sgYXQga2V5IG1hcmtlcnMgZm9yIGByIHNhdmVfbmFtZWAgY2VsbHMsIGFuZCBmb3IgZXZlbnR1YWwgY29udGFtaW5hdGlvbiA6CgpgYGB7ciBwbG90X2dlbmVzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CnBsb3RfbGlzdCA9IGxhcHBseShjKCJuRmVhdHVyZV9STkEiLCBvcnNfaWZlYl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICBjKCJTUElOSzUiLCAiS1JUMSIsICJLUlREQVAiLCAiQ0lERUEiKSksIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgICAgICAgICAgICAgICAgICAgICAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKHJlZHVjdGlvbl9uYW1lLCAiXyIsIG5kaW1zLCAiX3VtYXAiKSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICAgICAgICAgICAgICAgICAgICAgICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCiAgICAgICAgICAgICAgICAgICAgIH0pCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBucm93ID0gMikKYGBgCgpXZSBzZWxlY3QgY2x1c3RlcnMgYmFzZWQgb24gS1JUREFQIGV4cHJlc3Npb24gOgoKYGBge3Igc2VsZWN0X2NsdXN0ZXJzLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9Cm1lYW5fc2NvcmVfdGhyZXNoID0gMQoKc29iaiRLUlREQVBfZXhwciA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmosICJLUlREQVAiKQpzY29yZV9ieV9jbHVzdGVycyA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpzZWxlY3Qoc2V1cmF0X2NsdXN0ZXJzLCBLUlREQVBfZXhwcikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHNldXJhdF9jbHVzdGVycykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShhdmdfc2NvcmUgPSBtZWFuKEtSVERBUF9leHByKSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCgpnZ3Bsb3QyOjpnZ3Bsb3Qoc2NvcmVfYnlfY2x1c3RlcnMsIGFlcyh4ID0gc2V1cmF0X2NsdXN0ZXJzLCB5ID0gYXZnX3Njb3JlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dwbG90Mjo6Z2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbl9zY29yZV90aHJlc2gsIGNvbCA9ICJyZWQiKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIkNsdXN0ZXIgSUQiLCB5ID0gIk1lYW4gS1JUREFQIGV4cHJlc3Npb24iLAogICAgICAgICAgICAgICAgdGl0bGUgPSAiUHJvcG9ydGlvbiBvZiBjZWxscyBieSBjbHVzdGVyIikgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpCmBgYAoKV2UgcmVtb3ZlIGNsdXN0ZXJzIGFib3ZlIHRocmVzaG9sZCA6CgpgYGB7ciBjbHVzdGVyc19yZW1vdmFsfQpjbHVzdGVyc190b19yZW1vdmUgPSBzY29yZV9ieV9jbHVzdGVycyAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19zY29yZSA+IG1lYW5fc2NvcmVfdGhyZXNoKSAlPiUKICBkcGx5cjo6cHVsbChzZXVyYXRfY2x1c3RlcnMpICU+JQogIGFzLmNoYXJhY3RlcigpCgpzb2JqID0gc3Vic2V0KHNvYmosIGlkZW50cyA9IGNsdXN0ZXJzX3RvX3JlbW92ZSwgaW52ZXJ0ID0gVFJVRSkKc29iagpgYGAKCldlIHJlbW92ZSBhbGwgcmVkdWN0aW9ucyBpbiB0aGlzIGRhdGFzZXQgOgoKYGBge3IgZGlldF9zb2JqfQpzb2JqID0gU2V1cmF0OjpEaWV0U2V1cmF0KHNvYmosIGFzc2F5cyA9ICJSTkEiKQpzb2JqCmBgYAoKSG93IG1hbnkgY2VsbHMgYnkgc2FtcGxlID8KCmBgYHtyIHRhYmxlX29yaWdfaWRlbnQyfQp0YWJsZShzb2JqJHByb2plY3RfbmFtZSkKYGBgCgpXZSByZXByZXNlbnQgdGhpcyBpbmZvcm1hdGlvbiBhcyBhIGJhcnBsb3QgOgoKYGBge3IgYmFycGxvdF9jb3VudDIsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQphcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gdGFibGUoc29iaiRwcm9qZWN0X25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGNlbGxfdHlwZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBgY29sbmFtZXM8LWAoYygicHJvamVjdF9uYW1lIiwgImNlbGxfdHlwZSIsICJuYl9jZWxscyIpKSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gInByb2plY3RfbmFtZSIsIHkgPSAibmJfY2VsbHMiLCBmaWxsID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCB0eXBlIikKYGBgCgpObyBjb250YW1pbmF0aW9uIHJlbWFpbmluZyAhCgojIyBQcm9qZWN0aW9uCgpXZSByZW1vdmUgZ2VuZXMgdGhhdCBhcmUgZXhwcmVzc2VkIGluIGxlc3MgdGhhbiA1IGNlbGxzIDoKCmBgYHtyIGZpbHRlcl9nZW5lc30Kc29iaiA9IGFxdWFyaXVzOjpmaWx0ZXJfZmVhdHVyZXMoc29iaiwgbWluX2NlbGxzID0gNSkKc29iagpgYGAKCgpXZSBub3JtYWxpemUgdGhlIGNvdW50IG1hdHJpeCBmb3IgcmVtYWluaW5nIGNlbGxzIDoKCmBgYHtyIG5vcm1hbGl6YXRpb24yfQpzb2JqID0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIikKc29iaiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoc29iaiwgbmZlYXR1cmVzID0gMjAwMCkKc29iaiA9IFNldXJhdDo6U2NhbGVEYXRhKHNvYmopCgpzb2JqCmBgYAoKV2UgcGVyZm9ybSBhIFBDQSA6CgpgYGB7ciBwY2EyfQpzb2JqID0gU2V1cmF0OjpSdW5QQ0Eoc29iaiwKICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9ICJSTkFfcGNhIiwKICAgICAgICAgICAgICAgICAgICAgIG5wY3MgPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMKQpzb2JqCmBgYAoKV2UgY2hvb3NlIHRoZSBudW1iZXIgb2YgZGltZW5zaW9ucyBzdWNoIHRoYXQgdGhleSBzdW1tYXJpemUgMzUgJSBvZiB0aGUgdmFyaWFiaWxpdHkgOgoKYGBge3IgbmRpbXMyfQpzdGRldiA9IHNvYmpAcmVkdWN0aW9uc1tbIlJOQV9wY2EiXV1Ac3RkZXYKc3RkZXZfcHJvcCA9IGN1bXN1bShzdGRldikvc3VtKHN0ZGV2KQpuZGltcyA9IHdoaWNoKHN0ZGV2X3Byb3AgPiAwLjM1KVsxXQpuZGltcwpgYGAKCldlIGNhbiB2aXN1YWxpemUgdGhpcyBvbiB0aGUgZWxib3cgcGxvdCA6CgpgYGB7ciBlbGJvd3Bsb3QyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CmVsYm93X3AgPSBTZXVyYXQ6OkVsYm93UGxvdChzb2JqLCBuZGltcyA9IDEwMCwgcmVkdWN0aW9uID0gIlJOQV9wY2EiKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludCh4ID0gbmRpbXMsIHkgPSBzdGRldltuZGltc10sIGNvbCA9ICJyZWQiKQp4X3RleHQgPSBnZ3Bsb3RfYnVpbGQoZWxib3dfcCkkbGF5b3V0JHBhbmVsX3BhcmFtc1tbMV1dJHgkZ2V0X2xhYmVscygpICU+JSBhcy5udW1lcmljKCkKZWxib3dfcCA9IGVsYm93X3AgKwogIGdncGxvdDI6OnNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzb3J0KGMoeF90ZXh0LCBuZGltcykpLCBsaW1pdHMgPSBjKDAsIDEwMCkpCnhfY29sb3IgPSBpZmVsc2UoZ2dwbG90X2J1aWxkKGVsYm93X3ApJGxheW91dCRwYW5lbF9wYXJhbXNbWzFdXSR4JGdldF9sYWJlbHMoKSAlPiUKICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoKSAlPiUgcm91bmQoLiwgMikgPT0gcm91bmQobmRpbXMsIDIpLCAicmVkIiwgImJsYWNrIikKZWxib3dfcCA9IGVsYm93X3AgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3IgPSB4X2NvbG9yKSkKCmVsYm93X3AKYGBgCgojIyMgV2l0aG91dCBjb3JyZWN0aW9uCgpXZSBnZW5lcmF0ZSBhIHRTTkUgYW5kIGEgVU1BUCB3aXRoIGByIG5kaW1zYCBwcmluY2lwYWwgY29tcG9uZW50cyA6CgpgYGB7ciB0c25lX3VtYXAyfQpzb2JqID0gU2V1cmF0OjpSdW5UU05FKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gIlJOQV9wY2EiLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm5kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMTMzN0wsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLm5hbWUgPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdHNuZSIpKQoKc29iaiA9IFNldXJhdDo6UnVuVU1BUChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJSTkFfcGNhIiwKICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMTpuZGltcywKICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi5uYW1lID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3VtYXAiKSkKYGBgCgpXZSBjYW4gdmlzdWFsaXplIHRoZSB0d28gcmVwcmVzZW50YXRpb25zIDoKCmBgYHtyIHNlZV91bWFwX3RzbmUyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnRzbmUgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAicHJvamVjdF9uYW1lIiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdHNuZSIpKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgZ2dwbG90Mjo6Z2d0aXRsZSgiUENBIC0gdFNORSIpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnVtYXAgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAicHJvamVjdF9uYW1lIiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgZ2dwbG90Mjo6Z2d0aXRsZSgiUENBIC0gVU1BUCIpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKdHNuZSB8IHVtYXAKYGBgCgpUaGVyZSBpcyBhIGJhdGNoLWVmZmVjdCBtYWlubHkgaW4gdGhlIHJpZ2h0IHBvcHVsYXRpb24gOiBkdWUgdG8gdGhlIGRpc2Vhc2UgPwoKIyMjIEhhcm1vbnkKCldlIHJlbW92ZSBiYXRjaC1lZmZlY3QgdXNpbmcgSGFybW9ueSA6CgpgYGB7ciBoYXJtb255LCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KYCV8fCVgID0gZnVuY3Rpb24obGhzLCByaHMpIHsKICBpZiAoIWlzLm51bGwoeCA9IGxocykpIHsKICAgIHJldHVybihsaHMpCiAgfSBlbHNlIHsKICAgIHJldHVybihyaHMpCiAgfQp9CgpzZXQuc2VlZCgxMzM3TCkKc29iaiA9IGhhcm1vbnk6OlJ1bkhhcm1vbnkob2JqZWN0ID0gc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkudmFycyA9ICJwcm9qZWN0X25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2NvbnZlcmdlbmNlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gIlJOQV9wY2EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheS51c2UgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLnNhdmUgPSAiaGFybW9ueSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5pdGVyLmhhcm1vbnkgPSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdC5kaW0gPSBGQUxTRSkKYGBgCgpGcm9tIHRoaXMgYmF0Y2gtZWZmZWN0IHJlbW92ZWQgcHJvamVjdGlvbiwgd2UgZ2VuZXJhdGUgYSB0U05FIGFuZCBhIFVNQVAuCgpgYGB7ciBoYXJtb255X3RzbmVfdW1hcCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0Kc29iaiA9IFNldXJhdDo6UnVuVU1BUChzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm5kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJoYXJtb255IiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9IHBhc3RlMCgiaGFybW9ueV8iLCBuZGltcywgIl91bWFwIiksCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLmtleSA9IHBhc3RlMCgiaGFybW9ueV8iLCBuZGltcywgInVtYXBfIikpCnNvYmogPSBTZXVyYXQ6OlJ1blRTTkUoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMTpuZGltcywKICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEzMzdMLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJoYXJtb255IiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9IHBhc3RlMCgiaGFybW9ueV8iLCBuZGltcywgIl90c25lIiksCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLmtleSA9IHBhc3RlMCgiaGFybW9ueSIsIG5kaW1zLCAidHNuZV8iKSkKYGBgCgpUaGVzZSBhcmUgdGhlIGNvcnJlY3RlZCBVTUFQIGFuZCB0U05FIDoKCmBgYHtyIHNlZV91bWFwX3RzbmVfaGFybW9ueTEsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KdHNuZSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJwcm9qZWN0X25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IHBhc3RlMCgiaGFybW9ueV8iLCBuZGltcywgIl90c25lIikpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJQQ0EgLSBoYXJtb255IC0gdFNORSIpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnVtYXAgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAicHJvamVjdF9uYW1lIiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoImhhcm1vbnlfIiwgbmRpbXMsICJfdW1hcCIpKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgZ2dwbG90Mjo6Z2d0aXRsZSgiUENBIC0gaGFybW9ueSAtIFVNQVAiKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRzbmUgfCB1bWFwCmBgYAoKCldlIHdpbGwga2VlcCB0aGUgdFNORSBmcm9tIEhhcm1vbnkgOgoKYGBge3Igc2V0X25hbWUyRH0KcmVkdWN0aW9uID0gImhhcm1vbnkiCm5hbWUyRCA9IHBhc3RlMCgiaGFybW9ueV8iLCBuZGltcywgIl90c25lIikKYGBgCgojIyBDbHVzdGVyaW5nCgpXZSBnZW5lcmF0ZSBhIGNsdXN0ZXJpbmcgOgoKYGBge3IgY2x1c3RlcmluZzIsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpzb2JqID0gU2V1cmF0OjpGaW5kTmVpZ2hib3JzKHNvYmosIHJlZHVjdGlvbiA9IHJlZHVjdGlvbiwgZGltcyA9IDE6bmRpbXMpCnNvYmogPSBTZXVyYXQ6OkZpbmRDbHVzdGVycyhzb2JqLCByZXNvbHV0aW9uID0gMSkKCmRpbXBsb3RfY2x1c3RlcnMgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBsYWJlbCA9IFRSVUUpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpkaW1wbG90X2NsdXN0ZXJzCmBgYAoKCiMgVmlzdWFsaXphdGlvbgoKV2UgY2FuIHJlcHJlc2VudCB0aGUgNCBxdWFsaXR5IG1ldHJpY3MgOgoKYGBge3IgcWNfcGxvdCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAzfQpwbG90X2xpc3QgPSBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21iaW5lID0gRkFMU0UsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJwZXJjZW50Lm10IiwgInBlcmNlbnQucmIiLCAibkZlYXR1cmVfUk5BIiwgImxvZ19uQ291bnRfUk5BIikpCnBsb3RfbGlzdCA9IGxhcHBseShwbG90X2xpc3QsIEZVTiA9IGZ1bmN0aW9uKG9uZV9wbG90KSB7CiAgb25lX3Bsb3QgKwogICAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6Ojpjb2xvcl9nZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbnJvdyA9IDEpCmBgYAoKIyMgUHJvamVjdCBuYW1lCgpXZSBjYW4gdmlzdWFsaXplIHRoZSB0d28gYmF0Y2gtZWZmZWN0IGNvcnJlY3RlZCByZXByZXNlbnRhdGlvbnMgOgoKYGBge3Igc2VlX3VtYXBfdHNuZV9hbGwsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KcGxvdF9saXN0ID0gbGFwcGx5KGMocGFzdGUwKCJoYXJtb255XyIsIG5kaW1zLCAiX3RzbmUiKSwKICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJoYXJtb255XyIsIG5kaW1zLCAiX3VtYXAiKSksIEZVTiA9IGZ1bmN0aW9uKG9uZV9wcm9qKSB7CiAgICAgICAgICAgICAgICAgICAgICAgU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gInByb2plY3RfbmFtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG9uZV9wcm9qKSArCiAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKSArCiAgICAgICAgICAgICAgICAgICAgICAgICBTZXVyYXQ6Ok5vQXhlcygpICsgZ2dwbG90Mjo6Z2d0aXRsZShvbmVfcHJvaikgKwogICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogICAgICAgICAgICAgICAgICAgICB9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIpCmBgYAoKIyMgQ2VsbCB0eXBlCgpXZSBhbHNvIHZpc3VhbGl6ZSBjZWxsIHR5cGVzIDoKCmBgYHtyIHNlZV91bWFwX3RzbmVfY2VsbHR5cGUsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KcGxvdF9saXN0ID0gbGFwcGx5KGMocGFzdGUwKCJoYXJtb255XyIsIG5kaW1zLCAiX3RzbmUiKSwKICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJoYXJtb255XyIsIG5kaW1zLCAiX3VtYXAiKSksIEZVTiA9IGZ1bmN0aW9uKG9uZV9wcm9qKSB7CiAgICAgICAgICAgICAgICAgICAgICAgU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG9uZV9wcm9qKSArCiAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgU2V1cmF0OjpOb0F4ZXMoKSArIGdncGxvdDI6OmdndGl0bGUob25lX3Byb2opICsKICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAgICAgICAgICAgICAgICAgICAgfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAyKQpgYGAKCiMjIENsdXN0ZXJzCgpXZSBjYW4gcmVwcmVzZW50IGNsdXN0ZXJzLCBzcGxpdCBieSBzYW1wbGUgb2Ygb3JpZ2luIDoKCmBgYHtyIHBsb3Rfc3BsaXRfZGltcmVkLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDd9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X3NwbGl0X2RpbXJlZChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSAic2FtcGxlX2lkZW50aWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2NvbG9yID0gc2V0TmFtZXMoc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5tID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfY29sb3IgPSBhcXVhcml1czo6Z2dfY29sb3JfaHVlKGxlbmd0aChsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluX3B0X3NpemUgPSAwLjUsIGJnX3B0X3NpemUgPSAwLjUpCgpwbG90X2xpc3RbW2xlbmd0aChwbG90X2xpc3QpICsgMV1dID0gZGltcGxvdF9jbHVzdGVycwoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDQpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgJgogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpXZSBtYWtlIGEgaGVhdG1hcCB0byBzZWUgY2x1c3RlcnMgZGlzdHJpYnV0aW9uIGFtb25nIHNhbXBsZXMgOgoKYGBge3IgYW5ub3RhdGVkX2hlYXRtYXAsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTB9CmNsdXN0ZXJfbWFya2VycyA9IGMoIktSVDE0IiwKICAgICAgICAgICAgICAgICAgICAjIElGRSBiYXNhbC1yZWxhdGVkCiAgICAgICAgICAgICAgICAgICAgIktSVDE2IiwgIk1HUCIsICJLUlQ2QyIsICJDU1Q2IiwKICAgICAgICAgICAgICAgICAgICAjIElGRSBiYXNhbAogICAgICAgICAgICAgICAgICAgICJHUFgyIiwgIkMxUVRORjEyIiwgIlBUTiIsICJDTEVDMkIiLCAiVEdGQkkiLAogICAgICAgICAgICAgICAgICAgICMgUUMgbWV0cmljcwogICAgICAgICAgICAgICAgICAgICJUT1AyQSIsICJNQ001IiwKICAgICAgICAgICAgICAgICAgICAicGVyY2VudC5tdCIsICJwZXJjZW50LnJiIiwgImxvZ19uQ291bnRfUk5BIikKCmh0X2Fubm90ID0gU2V1cmF0OjpGZXRjaERhdGEoc29iaiwgc2xvdCA9ICJkYXRhIiwgdmFycyA9IGNsdXN0ZXJfbWFya2VycykgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCmh0X2Fubm90JGNsdXN0ZXJzID0gc29iaiRzZXVyYXRfY2x1c3RlcnMKaHRfYW5ub3QgPSBodF9hbm5vdCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3RlcnMpICU+JQogIGRwbHlyOjpzdW1tYXJpc2VfYWxsKGZ1bnMoJ21lYW4nID0gbWVhbikpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1jbHVzdGVycykgJT4lCiAgYGNvbG5hbWVzPC1gKGMoY2x1c3Rlcl9tYXJrZXJzKSkKaGVhZChodF9hbm5vdCkKCmNvbG9yX2Z1biA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgZ2VuZV9yYW5nZSA9IHJhbmdlKGh0X2Fubm90Wywgb25lX2dlbmVdKQogIGdlbmVfcGFsZXR0ZSA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGNvbG9ycyA9IGMoIiNGRkZGRkYiLCBhcXVhcml1czo6Y29sb3JfZ2VuZVstMV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcShmcm9tID0gZ2VuZV9yYW5nZVsxXSwgdG8gPSBnZW5lX3JhbmdlWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gbGVuZ3RoKGFxdWFyaXVzOjpjb2xvcl9nZW5lKSkpCiAgcmV0dXJuKGdlbmVfcGFsZXR0ZSkKfQoKaGEgPSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oZGYgPSBodF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2ggPSAiY29sdW1uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBzZXROYW1lcyhubSA9IGNsdXN0ZXJfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFwcGx5KGNsdXN0ZXJfbWFya2VycywgRlVOID0gY29sb3JfZnVuKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiKQoKaHQgPSBhcXVhcml1czo6cGxvdF9wcm9wX2hlYXRtYXAoZGYgPSBzb2JqQG1ldGEuZGF0YVssIGMoInNhbXBsZV9pZGVudGlmaWVyIiwgInNldXJhdF9jbHVzdGVycyIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tX2Fubm90YXRpb24gPSBoYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcF9tYXJnaW4gPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKG5hbWVzID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfdGl0bGUgPSAiU2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkNsdXN0ZXIiKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZHMgPSBUUlVFKQpgYGAKCldlIGFsc28gbG9vayBhdCBnZW5lcyBvZiBpbnRlcmVzdCBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBwbG90X2dlbmVzX29pLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDd9CnBsb3RfbGlzdCA9IGxhcHBseShjbHVzdGVyX21hcmtlcnMsIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgcCA9IFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwdC5zaXplID0gMC4yLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQogIAogIHJldHVybihwKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDUpCmBgYAoKIyBTYXZlCgpXZSBzYXZlIHRoZSBTZXVyYXQgb2JqZWN0IDoKCmBgYHtyIHNhdmVfc29ian0Kc2F2ZVJEUyhzb2JqLCBmaWxlID0gcGFzdGUwKG91dF9kaXIsICIvIiwgc2F2ZV9uYW1lLCAiX3NvYmoucmRzIikpCmBgYAoKCiMgUiBTZXNzaW9uCgpgYGB7ciBzZXNzaW9uaW5mbywgZWNobyA9IEZBTFNFLCBmb2xkX291dHB1dCA9IFRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCgo=